package com.xyazm.market.service.impl;

import com.xyazm.common.core.JsonResult;
import com.xyazm.common.enums.AmountTypeEnum;
import com.xyazm.common.utils.CheckResultUtil;
import com.xyazm.common.utils.ParamCheckUtil;
import com.xyazm.coupon.api.IUserCouponApi;
import com.xyazm.coupon.domain.dto.UserCouponDTO;
import com.xyazm.coupon.domain.param.QueryUserCouponParam;
import com.xyazm.market.constants.MarketConstant;
import com.xyazm.market.dao.FreightTemplateDAO;
import com.xyazm.market.domain.entity.FreightTemplateDO;
import com.xyazm.market.mapstruct.MarketApiMapstruct;
import com.xyazm.market.service.IMarketService;
import com.xyazm.market.domain.dto.CalculateOrderAmountDTO;
import com.xyazm.market.domain.param.CalculateOrderAmountParam;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class MarketServiceImpl implements IMarketService {

    /**
     * 优惠券服务
     */
    @DubboReference(version = "1.0.0")
    private IUserCouponApi userCouponApi;

    @Autowired
    private FreightTemplateDAO freightTemplateDAO;

    /**
     * 计算订单费用
     *
     * 假设订单有两个商品条目记录，分摊优惠券的规则如下：
     * 商品1
     * 单价（单位分）    购买数量    小计
     * 1000            10         1000 * 10
     *
     * 商品2
     * 单价    购买数量    小计
     * 100    1         100 * 1
     *
     *
     * 整单优惠券抵扣5元，也就是500分
     *
     * 则商品1分摊的优惠券抵扣金额为：
     * 优惠券抵扣总额 * (商品1单价*商品1购买数量)/((商品1单价*商品1购买数量) + (商品2单价*商品2购买数量))
     * = 500 * (1000 * 10) / ((1000 * 10)  + (100 * 1) )
     * = 5000000 / 10100
     * = 495 分
     *
     * 同样的逻辑可计算出商品2分摊的优惠券抵扣金额为5分，也就是0.05元
     *
     *
     * 如果计算出的优惠券分摊到一条 item 上存在小数时，则向上取整，然后最后一条 item 分摊的金额就用优惠金额减掉前面所有优惠的item分摊的总额
     *
    */
    @Override
    public CalculateOrderAmountDTO calculateOrderAmount(CalculateOrderAmountParam param) {
        // 计算订单价格入参检查
        checkCalculateOrderAmountParam(param);
        String orderId = param.getOrderId();
        // 如果使用优惠券,则计算优惠券抵扣金额
        String couponId = param.getCouponId();
        // 优惠券抵扣金额(分)
        Integer couponDiscountAmount = 0;
        if (StringUtils.isNotEmpty(couponId)) {
            // 查询优惠券详细信息
            UserCouponDTO userCoupon = getUserCoupon(param.getUserId(), param.getCouponId());
            couponDiscountAmount = userCoupon.getAmount();
        }

        // 订单条目费用信息
        List<CalculateOrderAmountParam.OrderItemParam> orderItemParamList =
                param.getOrderItemParamList();
        // 统计全部商品费用
        int totalProductAmount = 0;
        for (CalculateOrderAmountParam.OrderItemParam orderItemParam : orderItemParamList) {
            totalProductAmount += orderItemParam.getSalePrice() * orderItemParam.getSaleQuantity();
        }

        int index = 0;
        int totalNum = orderItemParamList.size();
        // 优惠券剩余可抵扣金额
        Integer notLastItemTotalDiscountAmount = 0;
        // 订单条目费用信息
        List<CalculateOrderAmountDTO.OrderAmountDetailDTO> orderAmountDetailDTOList = new ArrayList<>();

        for (CalculateOrderAmountParam.OrderItemParam orderItemParam : orderItemParamList) {
            // 组装订单条目费用明细 - 订单支付原价
            CalculateOrderAmountDTO.OrderAmountDetailDTO originPayAmountDetail = createOrderAmountDetailDTO(
                    orderId,
                    AmountTypeEnum.ORIGIN_PAY_AMOUNT.getCode(),
                    null,
                    null, orderItemParam);
            orderAmountDetailDTOList.add(originPayAmountDetail);

            // 组装订单条目费用明细 - 优惠券抵扣金额
            CalculateOrderAmountDTO.OrderAmountDetailDTO couponDiscountAmountDetail = null;
            if(++index < totalNum) {
                // 订单条目分摊的优惠金额

                // 优惠券抵扣总金额 * 商品单价 * 商品购买数量 / 订单总金额)
                double partDiscountAmount = Integer.valueOf(couponDiscountAmount
                        * orderItemParam.getSalePrice() * orderItemParam.getSaleQuantity()).doubleValue()
                        / Integer.valueOf(totalProductAmount).doubleValue();
                // 遇到小数则向上取整
                double curDiscountAmount = Math.ceil(partDiscountAmount);
                couponDiscountAmountDetail = createOrderAmountDetailDTO(
                                orderId,
                                AmountTypeEnum.COUPON_DISCOUNT_AMOUNT.getCode(),
                                Double.valueOf(curDiscountAmount).intValue(),
                                null,
                                orderItemParam);
                notLastItemTotalDiscountAmount += couponDiscountAmountDetail.getAmount();
            } else {
                // 最后一条item的优惠金额等于总优惠金额-前面所有item分摊的优惠总额
                couponDiscountAmountDetail = createOrderAmountDetailDTO(
                                orderId,
                                AmountTypeEnum.COUPON_DISCOUNT_AMOUNT.getCode(),
                                couponDiscountAmount - notLastItemTotalDiscountAmount,
                                null,
                                orderItemParam);
            }
            orderAmountDetailDTOList.add(couponDiscountAmountDetail);

            // 组装订单条目费用明细 - 实付金额
            // 实付金额 = 订单支付原价 - 优惠券抵扣金额
            Integer realPayAmount = originPayAmountDetail.getAmount() - couponDiscountAmountDetail.getAmount();
            CalculateOrderAmountDTO.OrderAmountDetailDTO realPayAmountDetail = createOrderAmountDetailDTO(
                            orderId,
                            AmountTypeEnum.REAL_PAY_AMOUNT.getCode(),
                            null,
                            realPayAmount,
                            orderItemParam);
            orderAmountDetailDTOList.add(realPayAmountDetail);
        }

        // 重新计算订单支付原价、优惠券抵扣金额、实付金额
        Integer totalOriginPayAmount = 0;
        Integer totalDiscountAmount = 0;
        Integer totalRealPayAmount = 0;
        for(CalculateOrderAmountDTO.OrderAmountDetailDTO orderAmountDetailDTO : orderAmountDetailDTOList) {
            // 收费类型
            Integer amountType = orderAmountDetailDTO.getAmountType();
            // 收费金额
            Integer amount = orderAmountDetailDTO.getAmount();
            if(AmountTypeEnum.ORIGIN_PAY_AMOUNT.getCode().equals(amountType)) {
                totalOriginPayAmount += amount;
            } else if(AmountTypeEnum.COUPON_DISCOUNT_AMOUNT.getCode().equals(amountType)) {
                totalDiscountAmount += amount;
            } else if(AmountTypeEnum.REAL_PAY_AMOUNT.getCode().equals(amountType)) {
                totalRealPayAmount += amount;
            }
        }
        // 总的实付金额还要加上运费
        // key: 收费类型  value: 订单费用信息
        List<CalculateOrderAmountDTO.OrderAmountDTO> orderAmountDTOList =
                MarketApiMapstruct.INSTANCE.convertOrderAmountDTO(param.getOrderAmountParamList());
        Map<Integer, CalculateOrderAmountDTO.OrderAmountDTO> orderAmountMap =
                orderAmountDTOList.stream().collect(Collectors.toMap(
                        CalculateOrderAmountDTO.OrderAmountDTO::getAmountType, Function.identity()));
        // 计算运费
        Integer shippingAmount = calculateOrderShippingAmount(param.getRegionId(), orderAmountMap);
        if(shippingAmount != null) {
            totalRealPayAmount += shippingAmount;
        }

        for(CalculateOrderAmountDTO.OrderAmountDTO orderAmountDTO : orderAmountDTOList) {
            Integer amountType = orderAmountDTO.getAmountType();
            if(AmountTypeEnum.ORIGIN_PAY_AMOUNT.getCode().equals(amountType)) { // 订单支付原价
                orderAmountDTO.setAmount(totalOriginPayAmount);
            } else if(AmountTypeEnum.COUPON_DISCOUNT_AMOUNT.getCode().equals(amountType)) { // 优惠券抵扣金额
                orderAmountDTO.setAmount(totalDiscountAmount);
            } else if(AmountTypeEnum.REAL_PAY_AMOUNT.getCode().equals(amountType)) { // 实付金额
                orderAmountDTO.setAmount(totalRealPayAmount);
            }
            // 设置订单id
            orderAmountDTO.setOrderId(orderId);
        }

        CalculateOrderAmountDTO calculateOrderAmountDTO = new CalculateOrderAmountDTO();
        calculateOrderAmountDTO.setOrderAmountList(orderAmountDTOList);
        calculateOrderAmountDTO.setOrderAmountDetail(orderAmountDetailDTOList);
        return calculateOrderAmountDTO;
    }


    /**
     * 计算订单价格入参检查
     * @author xuyou
     * 2022/6/1 0001 21:36
     * @param calculateOrderAmountParam
    */
    private void checkCalculateOrderAmountParam(CalculateOrderAmountParam calculateOrderAmountParam) {
        // 订单ID
        String orderId = calculateOrderAmountParam.getOrderId();
        ParamCheckUtil.checkStringNonEmpty(orderId);

        // 用户ID
        String userId = calculateOrderAmountParam.getUserId();
        ParamCheckUtil.checkStringNonEmpty(userId);

        // 订单商品条目
        List<CalculateOrderAmountParam.OrderItemParam> orderItemParamList =
                calculateOrderAmountParam.getOrderItemParamList();
        ParamCheckUtil.checkCollectionNonEmpty(orderItemParamList);

        // 订单费用信息
        List<CalculateOrderAmountParam.OrderAmountParam> orderAmountParamList =
                calculateOrderAmountParam.getOrderAmountParamList();
        ParamCheckUtil.checkCollectionNonEmpty(orderAmountParamList);
    }

    /**
     * 查询用户优惠券
     * @author xuyou
     * 2022/6/1 0001 21:51
     * @param userId
     * @param couponId
     * @return com.xyazm.coupon.domain.dto.UserCouponDTO
    */
    private UserCouponDTO getUserCoupon(String userId,String couponId) {
        QueryUserCouponParam param = new QueryUserCouponParam();
        param.setUserId(userId);
        param.setCouponId(couponId);
        JsonResult<UserCouponDTO> userCouponResult = userCouponApi.getUserCoupon(param);
        // 检查api调用结果
        CheckResultUtil.checkResult(userCouponResult,"查询用户优惠券");
        return userCouponResult.getData();
    }


    /**
     * 组装订单条目费用明细
     * @author xuyou
     * 2022/6/1 0001 22:09
     * @param orderId 订单id
     * @param amountType 费用类型
     * @param discountAmount 优惠券抵扣金额
     * @param realPayAmount 实付金额
     * @param orderItemParam 订单条目
     * @return com.xyazm.product.domain.dto.CalculateOrderAmountDTO.OrderAmountDetailDTO
    */
    private CalculateOrderAmountDTO.OrderAmountDetailDTO createOrderAmountDetailDTO(
            String orderId,
            Integer amountType,
            Integer discountAmount,
            Integer realPayAmount,
            CalculateOrderAmountParam.OrderItemParam orderItemParam ) {

        // 计算出来的订单条目费用信息
        CalculateOrderAmountDTO.OrderAmountDetailDTO orderAmountDetailDTO =
                new CalculateOrderAmountDTO.OrderAmountDetailDTO();
        orderAmountDetailDTO.setOrderId(orderId);
        orderAmountDetailDTO.setProductType(orderItemParam.getProductType());
        orderAmountDetailDTO.setSkuCode(orderItemParam.getSkuCode());
        orderAmountDetailDTO.setSaleQuantity(orderItemParam.getSaleQuantity());
        orderAmountDetailDTO.setSalePrice(orderItemParam.getSalePrice());
        orderAmountDetailDTO.setAmountType(amountType);
        // 计算收费金额
        if(AmountTypeEnum.ORIGIN_PAY_AMOUNT.getCode().equals(amountType)) {  // 订单支付原价
            orderAmountDetailDTO.setAmount(orderAmountDetailDTO.getSaleQuantity()
                    * orderAmountDetailDTO.getSalePrice());
        } else if(AmountTypeEnum.COUPON_DISCOUNT_AMOUNT.getCode().equals(amountType)) { // 优惠券抵扣金额
            orderAmountDetailDTO.setAmount(discountAmount);
        } else if(AmountTypeEnum.REAL_PAY_AMOUNT.getCode().equals(amountType)) { // 实付金额
            orderAmountDetailDTO.setAmount(realPayAmount);
        }
        return orderAmountDetailDTO;
    }

    /**
     * 计算运费
     * @author xuyou
     * 2022/6/1 0001 22:39
     * @param regionId 区域id
     * @param orderAmountMap key: 收费类型  value: 订单费用信息
     * @return java.lang.Integer
    */
    private Integer calculateOrderShippingAmount(String regionId, Map<Integer, CalculateOrderAmountDTO.OrderAmountDTO> orderAmountMap) {
        // 运费
        Integer shippingAmount;
        // 满多少免运费
        Integer conditionAmount;

        // 查找运费模板
        FreightTemplateDO freightTemplate = freightTemplateDAO.getByRegionId(regionId);
        if(freightTemplate != null) {
            shippingAmount = freightTemplate.getShippingAmount();
            conditionAmount = freightTemplate.getConditionAmount();
        } else {
            shippingAmount = MarketConstant.DEFAULT_SHIPPING_AMOUNT;
            conditionAmount = MarketConstant.DEFAULT_CONDITION_AMOUNT;
        }

        // 订单金额
        Integer originPayAmount = 0;
        CalculateOrderAmountDTO.OrderAmountDTO orderAmountDTO = orderAmountMap.get(AmountTypeEnum.ORIGIN_PAY_AMOUNT.getCode());
        if(orderAmountDTO != null) {
            originPayAmount = orderAmountDTO.getAmount();
        }
        // 如果原单金额大于指定值则免运费
        if(originPayAmount >= conditionAmount) {
            shippingAmount = 0;
        }
        return shippingAmount;
    }
}
